package huanzhao.erp.service;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import huanzhao.erp.domain.entity.Package;
import huanzhao.erp.domain.entity.*;
import huanzhao.erp.domain.po.PermissionCombine;
import huanzhao.erp.domain.po.Result;
import huanzhao.erp.domain.types.*;
import huanzhao.erp.domain.vo.TaskVO;
import huanzhao.erp.mapper.*;
import huanzhao.erp.util.CommonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.constraints.Range;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;

/**
 * @author April
 * 2024/6/17 下午10:00
 * @version 1.0
 */
@Service
@Slf4j
public class TaskService extends ServiceImpl<TaskMapper, Task> {
    final BasicService basicService;
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    private final ConcurrentHashMap<Long, ScheduledFuture<?>> scheduledTasks = new ConcurrentHashMap<>();
    private final NotificationService notificationService;
    private final TaskMapper taskMapper;
    private final NotificationMapper notificationMapper;
    private final ConvertService convertService;
    private final StaffMapper staffMapper;
    private final ExceptionMapper exceptionMapper;
    private final PositionService positionService;
    private final ClearService clearService;
    private final PhoneMapper phoneMapper;
    private final PhonePublishSoldMapper phonePublishSoldMapper;
    private final StockMapper stockMapper;
    private final MiscService miscService;
    private final PackageMapper packageMapper;
    private final CommentMapper commentMapper;
    private final TransferMapper transferMapper;
    private final TeamMapper teamMapper;
    private final TeamService teamService;
    private final PermissionService permissionService;
    private final PhoneService phoneService;

    public TaskService(BasicService basicService, NotificationService notificationService, TaskMapper taskMapper, NotificationMapper notificationMapper, ConvertService convertService, StaffMapper staffMapper, ExceptionMapper exceptionMapper, PositionService positionService, ClearService clearService, PhoneMapper phoneMapper, PhonePublishSoldMapper phonePublishSoldMapper, StockMapper stockMapper, MiscService miscService, PackageMapper packageMapper, CommentMapper commentMapper, TransferMapper transferMapper, TeamMapper teamMapper, TeamService teamService, PermissionService permissionService, PhoneService phoneService) {
        this.basicService = basicService;
        this.notificationService = notificationService;
        this.taskMapper = taskMapper;
        // 启动线程，跟踪未完成的任务
        new Thread(this::trackUncompletedTasks).start();
        this.notificationMapper = notificationMapper;
        this.convertService = convertService;
        this.staffMapper = staffMapper;
        this.exceptionMapper = exceptionMapper;
        this.positionService = positionService;
        this.clearService = clearService;
        this.phoneMapper = phoneMapper;
        this.phonePublishSoldMapper = phonePublishSoldMapper;
        this.stockMapper = stockMapper;
        this.miscService = miscService;
        this.packageMapper = packageMapper;
        this.commentMapper = commentMapper;
        this.transferMapper = transferMapper;
        this.teamMapper = teamMapper;
        this.teamService = teamService;
        this.permissionService = permissionService;
        this.phoneService = phoneService;
    }

    /**
     * 跟踪未完成的任务
     * 2024/7/28 下午6:29:48
     */
    private void trackUncompletedTasks() {
        List<Task> tasks = taskMapper.getUnreceivedLimitedTasks();
        if (tasks == null || tasks.isEmpty()) {
            return;
        }
        for (Task task : tasks) {
            // 计算剩余时间
            long timeLeft = task.getLimit() - (System.currentTimeMillis() - task.getCreateTime().getTime()) / 1000;
            Long taskId = task.getId();
            Long staffId = task.getReceiverId();
            if (timeLeft <= 0) {
                // 若未通知过才进行通知
                if (!notificationMapper.isNotified(taskId, NotificationType.TASK_TIMEOUT.getValue())) {
                    dealTimeoutTask(staffId, task);
                }
            } else {
                addScheduledTask(staffId, task, timeLeft);
            }
        }
    }


    /**
     * 按条件查询任务
     *
     * @param type      任务类型：质检、维修
     * @param stateCode 任务状态
     * @param all       是否查询所有任务
     * @param pageNum   当前页码
     * @param pageSize  每页记录数量
     * @return IPage<Task>
     * 2024/6/13 上午11:37:50
     */
    public IPage<TaskVO> getTasksByCondition(String type, Integer stateCode, boolean all, Integer pageNum, Integer pageSize) {
        IPage<Task> page = new Page<>();
        String state = null;
        if (stateCode != null) {
            state = TaskState.of(stateCode).getValue();
        }
        // todo： 此处需要确定员工能看到任务的最大范围
        if (all) {
            page = taskMapper.getTasksByCondition(type, state, Page.of(pageNum, pageSize));
        }
        List<TaskVO> result = new ArrayList<>();
        page.getRecords().forEach(task -> result.add(convertService.convertTask(task)));
        return new Page<TaskVO>().setRecords(result).setTotal(page.getTotal());
    }


    /**
     * 监测任务是否接收超时
     *
     * @param staffId  员工id
     * @param task     任务
     * @param timeLeft 剩余时间
     *                 2024/6/29 下午8:54:35
     */
    private void addScheduledTask(Long staffId, Task task, Long timeLeft) {
        Long taskId = task.getId();
        ScheduledFuture<?> scheduledFuture = scheduler.schedule(() -> {
            if (taskMapper.checkTaskIsRunning(taskId)) {
                dealTimeoutTask(staffId, task);
                // 移除定时任务
                scheduledTasks.remove(taskId);
            }
        }, timeLeft, TimeUnit.SECONDS);
        scheduledTasks.put(taskId, scheduledFuture);
    }

    /**
     * 处理超时任务
     *
     * @param staffId 员工id
     * @param task    任务
     *                2024/6/29 下午8:56:37
     */
    private void dealTimeoutTask(Long staffId, Task task) {
        Long taskId = task.getId();
        // 检查任务是否任然存在
        if (!taskMapper.existsById(taskId)) {
            return;
        }
        log.info("任务已超时，id：{}，类型：{}", taskId, task.getType());
        // 将任务状态设置为已超时
        taskMapper.setTaskTimeout(taskId);
        // 获取任务开始时间
        String startTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(task.getCreateTime().toInstant());
        String staffName = staffMapper.getNameById(staffId);
        if (staffName == null) {
            staffName = "已删除";
        } else {
            notificationService.sendNotification(staffId, NotificationType.TASK_TIMEOUT, "您有一条" + task.getType() + "任务超时未接收（任务开始时间：" + startTime + "），请尽快处理!", taskId);
        }
        basicService.recordOps(OperationTarget.TASK, OperationType.TIMEOUT, taskId);
        // 给直属上级发送通知
        Long superiorId = positionService.getSuperiorId(task);
        notificationService.sendNotification(superiorId, NotificationType.TASK_TIMEOUT, "员工" + staffName + "的" + task.getType() + "任务超时未接收（开始时间：" + startTime + "）！", taskId);
    }

    /**
     * 检查任务是否属于当前用户
     *
     * @param taskTypes   任务类型
     * @param completable 任务是否能被完成
     * @return Result<?>
     * 2024/10/8 20:19:51
     */
    public Result<?> checkTask(Long taskId, TaskType[] taskTypes, boolean completable) {
        if (taskId == null) {
            return Result.error("任务id为空");
        }
        Task task = taskMapper.getReceiverIdAndRejectAndStateById(taskId);
        if (task == null) {
            return Result.error("任务不存在");
        }
        if (!task.getReceiverId().equals(basicService.getCurrentStaffId())) {
            return Result.error("任务不属于自己");
        }
        // 若指定了任务类型，则检查任务类型是否符合
        if (taskTypes != null && taskTypes.length > 0) {
            for (TaskType taskType : taskTypes) {
                if (task.getType() == taskType) {
                    return Result.ok();
                }
            }
            return Result.error("任务类型应为：" + String.join("或", CommonUtil.toStringArray(taskTypes)));
        }
        if (completable) {
            // 检查任务是否能被完成
            if (task.getReject()) {
                return Result.error("流程异常，任务已拒收");
            }
            if (task.getState() != TaskState.PROCESSING) {
                return Result.error("任务状态异常，应为：处理中！");
            }
            // 检查是否为大盘库
            if (clearService.isInBigClear()) {
                return Result.error("大盘库期间，无法完成任务");
            }
        }
        return Result.ok();
    }

    /**
     * 接收任务
     * 此时一定有上一个任务
     * 已确保当前任务存在且属于当前用户
     *
     * @param take    是否接收
     * @param comment 若不接受，拒绝理由
     * @return Result<?>
     * 2024/10/8 22:58:24
     */
    public Result<?> takeTask(long taskId, boolean take, String comment) {
        Task currentTask = getById(taskId);
        if (currentTask.getState() != TaskState.WAIT_RECEIVE) {
            return Result.error("任务状态异常，应为：待接收！");
        }
        // 检查任务是否超时
        boolean overtime = currentTask.getOvertime() || (currentTask.getLimit() != null && System.currentTimeMillis() > (currentTask.getCreateTime().getTime() + currentTask.getLimit() * 1000));
        if (overtime) {
            // 若超时，则检查是否提交了超时异常工单
            boolean submitted = exceptionMapper.existsByTaskAndType(taskId, ExceptionType.TIMEOUT.getValue());

            // 若未提交，则发出提示
            if (!submitted) {
                return Result.error("任务已超时，请先进行超时报备");
            }
        } else {
            // 未超时则移除定时任务
            ScheduledFuture<?> scheduledTask = scheduledTasks.remove(currentTask.getId());
            if (scheduledTask != null) {
                scheduledTask.cancel(false);
            }
        }

        Long lastTaskId = currentTask.getLastTaskId();
        // 若接收任务，则将当前任务标记为处理中，将其标记为已完成
        if (take) {
            taskMapper.updateTaskState(taskId, TaskState.PROCESSING.getValue());
            taskMapper.updateTaskState(lastTaskId, TaskState.FINISHED.getValue());
            // 记录接收任务的操作
            basicService.recordOps(OperationTarget.TASK, OperationType.TAKE, taskId);
        }
        // 若不接收任务，则将当前任务和上一个任务都标记为拒绝接收
        else {
            taskMapper.rejectTask(taskId, comment);
            taskMapper.setReject(lastTaskId);
            // 发送通知消息
            notificationService.sendNotification(taskMapper.getReceiverIdById(lastTaskId), NotificationType.TASK_REJECT, "您的处于移交中的手机(id:" + currentTask.getPhoneId() + ")已被拒绝接收，原因：" + comment, taskId);
            // 记录拒绝任务的操作
            basicService.recordOps(OperationTarget.TASK, OperationType.REJECT, taskId, comment);
        }
        return Result.ok("操作成功");
    }

    /**
     * 分配任务
     *
     * @param phoneId    手机id
     * @param receiverId 任务接收方
     * @param taskType   任务类型
     * @param lastTask   上一条任务，可从中获取id与渠道id（若有）
     *                   2024/6/19 下午5:15:24
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void assignTask(long phoneId, long receiverId, TaskType taskType, Task lastTask) {
        // 1. 设置任务时限
        // 2. 设置任务负责人
        // 3. 将任务添加到数据库和队列中
        Long limit = basicService.getGlobalSetting().getLimit();
        if (taskType == TaskType.OUTSOURCE_REPAIR_SIGN) {
            // 若为外修签收任务，将时限设置为一周
            limit = 7 * 24 * 60 * 60L;
        }
        Task task = new Task(receiverId, phoneId, taskType, limit, lastTask);
        taskMapper.insert(task);
        addScheduledTask(receiverId, task, limit);

        log.info("分配任务，类型：{}，接收者id：{}，任务时限：{}，手机id：{}", taskType.getValue(), receiverId, limit, phoneId);

        // 记录接收任务的操作
        basicService.recordOps(receiverId, OperationTarget.TASK, OperationType.RECEIVE, task.getId());

        // 发送通知消息
        notificationService.sendNotification(receiverId, NotificationType.NEW_TASK, "您有一条新" + taskType.getValue() + "任务，请及时处理！", task.getId());
    }

    /**
     * 添加状态为移交中的任务
     * 用于流程的起始节点，以支持其后流程的报告异常事务
     * 支持的任务类型：分货、售后签收、仅出库、转库、处理退货
     *
     * @param phoneId    手机id
     * @param receiverId 任务接收方
     * @param taskType   任务类型
     * @param aftersale  是否售后任务
     * @return Task
     * 2024/10/9 20:10:49
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Task addTransferringTask(Long phoneId, Long receiverId, TaskType taskType, boolean aftersale) {
        Task task = new Task();
        task.setPhoneId(phoneId);
        task.setReceiverId(receiverId);
        task.setType(taskType);
        task.setAfterSale(aftersale);
        task.setState(TaskState.TRANSFERRING);

        // 将任务开始时间和完成时间都设置为当前
        Date now = Date.from(Instant.now());
        task.setCreateTime(now);
        task.setCompleteTime(now);

        // 将任务添加到数据库
        taskMapper.insertTransferringTask(task);

        // 记录完成任务的操作
        basicService.recordOps(receiverId, OperationTarget.TASK, OperationType.FINISH, task.getId());
        return task;
    }

    /**
     * 首次分配任务
     * 此时没有上一条任务信息
     *
     * @param phoneId    手机id
     * @param receiverId 任务接收方
     * @param taskType   任务类型
     *                   2024/10/8 21:06:28
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void assignTask(Long phoneId, Long receiverId, TaskType taskType) {
        assignTask(phoneId, receiverId, taskType, null);
    }

    /**
     * 售后任务首次分配
     * 此时没有上一条任务信息，且将售后标识设置为true
     *
     * @param phoneId    手机id
     * @param receiverId 任务接收方
     * @param taskType   任务类型
     *                   2024/10/8 21:44:15
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void assignAfterSaleTask(Long phoneId, Long receiverId, TaskType taskType) {
        Task lastTask = new Task();
        lastTask.setAfterSale(true);
        assignTask(phoneId, receiverId, taskType, lastTask);
    }

    /**
     * 分配任务至渠道
     *
     * @param phoneId    手机id
     * @param receiverId 任务接收方
     * @param taskType   任务类型
     * @param teamId     渠道id
     *                   2024/6/22 上午12:44:22
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void assignTask(long phoneId, long receiverId, TaskType taskType, long teamId) {
        Task lastTask = new Task();
        lastTask.setTeamId(teamId);
        assignTask(phoneId, receiverId, taskType, lastTask);
    }

    /**
     * 完成任务
     * 2024/6/19 下午7:44:23
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void finishTask(@NotNull Task task, String comment) {
        Long taskId = task.getId();
        taskMapper.finishTask(taskId, comment);
        // 记录任务完成日志
        basicService.recordOps(OperationTarget.TASK, OperationType.FINISH, taskId);
    }

    /**
     * 完成任务，且设置手机的状态
     *
     * @param newState 手机将要变为的状态
     *                 2024/10/10 19:44:02
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void finishTask(@NotNull Task task, PhoneState newState, String comment, PhoneState... currentStates) {
        phoneService.updatePhoneState(task.getPhoneId(), newState, currentStates);
        finishTask(task, comment);
    }

    /**
     * C端销售渠道组长指定质检员
     *
     * @param taskId  任务id
     * @param staffId 员工id
     * @return Result<?>
     * 2024/10/10 19:31:40
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> designateQCStaff(long taskId, long staffId, String comment) {
        Task currentTask = getById(taskId);
        // 获取任务的团队id
        Long teamId = currentTask.getTeamId();
        assert teamId != null;

        // 确保指定的员工是该团队的质检员
        Result<?> result = positionService.checkPosition(staffId, teamId, TeamPositionType.QUALITY_CHECKER);
        if (result.isError()) {
            return result;
        }

        // 分配质检任务
        assignTask(currentTask.getPhoneId(), staffId, TaskType.QC, currentTask);

        // 完成当前任务，设置手机状态为质检中
        finishTask(currentTask, PhoneState.QC, comment, PhoneState.WAIT_DESIGNATE_QC);
        return Result.ok("操作成功");
    }

    /**
     * 质检员对手机进行质检
     *
     * @param taskId    任务id
     * @param qualified 质检是否合格
     * @param staffId   质检合格为录入id，不合格为采购员id
     * @return Result<?>
     * 2024/10/10 19:48:48
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> checkPhone(long taskId, long staffId, boolean qualified, String comment) {
        Task currentTask = getById(taskId);
        if (qualified) {
            // 质检合格的情况下，将手机交给录入
            Result<?> result = positionService.checkPosition(staffId, currentTask.getTeamId(), TeamPositionType.ENTRY);
            if (result.isError()) {
                return result;
            }
            // 分配录入任务
            assignTask(currentTask.getPhoneId(), staffId, TaskType.ENTER, currentTask);
            // 将手机状态设置为录入中
            phoneService.updatePhoneState(currentTask.getPhoneId(), PhoneState.WAIT_ENTER, PhoneState.QC);
        } else {
            // 质检不合格的情况，需要采购送修
            Staff staff = staffMapper.selectById(staffId);
            if (!staff.getPurchaser()) {
                return Result.error("指定的员工不为采购员");
            }
            // 质检不通过，则任务不再属于该团队
            currentTask.setTeamId(null);
            // 采购送修任务
            assignTask(currentTask.getPhoneId(), staffId, TaskType.PURCHASER_SEND_REPAIR, currentTask);
            // 将手机状态设置为采购送修中
            phoneService.updatePhoneState(currentTask.getPhoneId(), PhoneState.PURCHASE_SEND_REPAIR, PhoneState.QC);
        }
        // 完成当前任务
        finishTask(currentTask, comment);
        return Result.ok("操作成功");
    }

    /**
     * 录入小组进行手机录入
     *
     * @param taskId  任务id
     * @param staffId 上架员id
     * @return Result<?>
     * 2024/10/10 20:02:58
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> enterPhone(long taskId, long staffId, String comment) {
        Task currentTask = getById(taskId);
        Result<?> result = positionService.checkPosition(staffId, currentTask.getTeamId(), TeamPositionType.PUBLISHER);
        if (result.isError()) {
            return result;
        }
        // 分配上架任务
        assignTask(currentTask.getPhoneId(), staffId, TaskType.PUBLISH, currentTask);
        // 完成当前任务，将手机状态设置为待上架
        finishTask(currentTask, PhoneState.WAIT_PUBLISH, comment, PhoneState.WAIT_ENTER);
        return Result.ok("操作成功");
    }

    /**
     * 上架小组将手机上架
     *
     * @param purchaserId 员工id：到哪个采购员
     * @param soldPrice   上架销售金额,，使用字符串表示
     * @param degree      上架成色
     * @return Result<?>
     * 2024/10/10 20:06:21
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> publish(long taskId, long purchaserId, BigDecimal soldPrice, @Range(max = 100) int degree, String comment) {
        Task currentTask = getById(taskId);
        // 添加上架记录
        phonePublishSoldMapper.insert(new PhonePublishSold(currentTask.getPhoneId(), true, soldPrice, currentTask.getTeamId(), degree));
        // 完成上架后，任务即不属于该渠道
        currentTask.setTeamId(null);
        // 分配接收上架任务
        assignTask(currentTask.getPhoneId(), purchaserId, TaskType.RECEIVE_PUBLISH, currentTask);
        // 完成当前任务，将手机状态设置为待确认上架
        finishTask(currentTask, PhoneState.WAIT_CONFIRM_PUBLISH, comment, PhoneState.WAIT_PUBLISH);
        return Result.ok("操作成功");
    }

    /**
     * 采购接收上架
     *
     * @param warehouserId 员工id：到哪个库管员
     * @return Result<?>
     * 2024/10/10 20:12:19
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> receivePublish(long taskId, long warehouserId, String comment) {
        Task currentTask = getById(taskId);
        // 分配入库任务
        assignTask(currentTask.getPhoneId(), warehouserId, TaskType.STOCK, currentTask);
        // 完成当前任务，将手机状态设置为待入库
        finishTask(currentTask, PhoneState.WAIT_STOCK, comment, PhoneState.WAIT_CONFIRM_PUBLISH);
        return Result.ok("操作成功");
    }

    /**
     * 库管将手机入库
     *
     * @param taskId      任务id
     * @param storageArea 存放区域
     * @param number      库位号
     * @return Result<?>
     * 2024/10/10 20:16:54
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> stockPhone(long taskId, @NotBlank String storageArea, String number, String comment) {
        Task currentTask = getById(taskId);
        // 添加入库记录
        stockMapper.insert(new Stock(currentTask.getPhoneId(), storageArea, number));

        // 完成当前任务，设置手机状态为已入库
        finishTask(currentTask, PhoneState.STOCKED, comment, PhoneState.WAIT_STOCK);
        return Result.ok("操作成功");
    }

    /**
     * 采购员将手机发给维修部门
     *
     * @param recheck 是否进行二次检查（用于重新分渠道）
     * @return Result<?>
     * 2024/10/10 20:26:45
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> sendToRepair(long taskId, boolean recheck, String comment) {
        Task currentTask = getById(taskId);
        PhoneState currentState = PhoneState.PURCHASE_SEND_REPAIR;
        if (recheck) {
            // 二次检查的情况，给自己分配二次检查任务
            assignTask(currentTask.getPhoneId(), basicService.getCurrentStaffId(), TaskType.RECHECK, currentTask);
            // 设置手机状态为二次检查中
            phoneService.updatePhoneState(currentTask.getPhoneId(), PhoneState.RE_CHECKING, currentState);
        } else {
            // 维修的情况，给维修组长分配维修评估任务
            assignTask(currentTask.getPhoneId(), basicService.getGlobalSetting().getRepairLeaderId(), TaskType.REPAIR_EVALUATE, currentTask);
            // 设置手机状态为维修评估中
            phoneService.updatePhoneState(currentTask.getPhoneId(), PhoneState.REPAIR_EVALUATING, currentState);
        }
        finishTask(currentTask, comment);
        return Result.ok();
    }

    /**
     * 维修主管对手机进行维修评估
     *
     * @param option  操作：0->维修，1->入库，2->报损
     * @param staffId 送回仓库时填写库管id，报损则不填
     * @return Result<?>
     * 2024/10/11 10:17:14
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> evaluateRepair(long taskId, int option, Long staffId, String comment) {
        Task currentTask = getById(taskId);
        PhoneState currentState = currentTask.getAfterSale() ? PhoneState.REPAIR_EVALUATING_IN_AS : PhoneState.REPAIR_EVALUATING;
        PhoneState state;
        switch (option) {
            case 0:
                // 给自己发出分派维修任务
                assignTask(currentTask.getPhoneId(), basicService.getCurrentStaffId(), TaskType.DESIGNATE_REPAIR, currentTask);
                // 设置手机状态为待分派维修
                state = currentTask.getAfterSale() ? PhoneState.WAIT_DESIGNATE_REPAIR_IN_AS : PhoneState.WAIT_DESIGNATE_REPAIR;
                phoneService.updatePhoneState(currentTask.getPhoneId(), state, currentState);
                break;
            case 1:
                // 入库的情况
                if (staffId == null) {
                    return Result.error("需指定库管员");
                }
                if (!staffMapper.selectById(staffId).getWarehouser()) {
                    return Result.error("指定的员工不为库管员");
                }
                // 给库管员分配接收维修任务
                assignTask(currentTask.getPhoneId(), staffId, TaskType.RECEIVE_REPAIR, currentTask);
                // 设置手机状态为库房待接收维修
                state = currentTask.getAfterSale() ? PhoneState.WAIT_RECEIVE_REPAIR_IN_AS : PhoneState.WAIT_RECEIVE_REPAIR;
                phoneService.updatePhoneState(currentTask.getPhoneId(), state, currentState);
                break;
            case 2:
                // 报损的情况
                // 添加异常事务给业务经理
                Long managerId = basicService.getGlobalSetting().getManagerId();
                miscService.sendException(managerId, taskId, ExceptionType.DESTROY, comment, null);
                break;
        }
        // 维修组长主动报损，任务应为正常完成，而不是异常
        finishTask(currentTask, comment);
        return Result.ok();
    }

    /**
     * 维修组长分派维修
     *
     * @param outsource 是否为送修
     * @param staffId   内部维修时为维修员id，送修时为库管id
     * @return Result<?>
     * 2024/10/11 10:53:01
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> dispatchRepair(long taskId, boolean outsource, long staffId, String comment) {
        Task currentTask = getById(taskId);
        Staff staff = staffMapper.selectById(staffId);
        PhoneState currentState = currentTask.getAfterSale() ? PhoneState.WAIT_DESIGNATE_REPAIR_IN_AS : PhoneState.WAIT_DESIGNATE_REPAIR;
        PhoneState state;
        if (outsource) {
            if (!staff.getWarehouser()) {
                return Result.error("指定的员工不为库管员");
            }
            // 送修的情况，给库管员分配送修任务
            assignTask(currentTask.getPhoneId(), staffId, TaskType.OUTSOURCE_REPAIR_SHIP, currentTask);
            // 设置手机状态为送修待发货
            state = currentTask.getAfterSale() ? PhoneState.WAIT_OUT_REPAIR_SHIP_IN_AS : PhoneState.WAIT_OUT_REPAIR_SHIP;
        } else {
            if (!staff.getMaintainer()) {
                return Result.error("指定的员工不为维修员");
            }
            // 维修的情况，给维修员分配维修任务
            assignTask(currentTask.getPhoneId(), staffId, TaskType.REPAIR, currentTask);
            // 设置手机状态为维修中
            state = currentTask.getAfterSale() ? PhoneState.REPAIRING_IN_AS : PhoneState.REPAIRING;
        }
        finishTask(currentTask, state, comment, currentState);
        return Result.ok();
    }

    /**
     * 完成手机维修
     * 对于普通的维修任务和外送维修回来的时候的维修完成填报任务均适用
     *
     * @param hasCost    是否增加维修成本
     * @param cost       增加的维修成本，用字符串表示
     * @param repaired   手机是否修好
     * @param costReason 维修成本说明
     * @return Result<?>
     * 2024/10/11 10:57:00
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> finishRepair(long taskId, boolean hasCost, BigDecimal cost, boolean repaired, String costReason, String comment) {
        Task currentTask = getById(taskId);
        PhoneState[] currentStates = currentTask.getAfterSale() ? new PhoneState[]{PhoneState.REPAIRING_IN_AS, PhoneState.WAIT_FILL_OUT_REPAIR_RESULT_IN_AS} : new PhoneState[]{PhoneState.REPAIRING, PhoneState.WAIT_FILL_OUT_REPAIR_RESULT};
        if (hasCost) {
            if (cost == null) {
                return Result.error("维修成本不能为空");
            }
            // 添加维修成本记录
            miscService.addFinanceRecord(currentTask.getPhoneId(), cost, FinanceSettlementType.REPAIR_COST, costReason);
        }
        String message = repaired ? "（已修好）" : "（未修好）";
        // 将message添加到备注的首部
        if (comment == null) {
            comment = message;
        } else {
            comment = message + comment;
        }
        // 给维修组长分配维修评估任务
        assignTask(currentTask.getPhoneId(), basicService.getGlobalSetting().getRepairLeaderId(), TaskType.REPAIR_EVALUATE, currentTask);

        // 设置手机状态为维修评估中
        PhoneState state = currentTask.getAfterSale() ? PhoneState.REPAIR_EVALUATING_IN_AS : PhoneState.REPAIR_EVALUATING;
        finishTask(currentTask, state, comment, currentStates);
        return Result.ok();
    }

    /**
     * 库管员发货送修的手机
     *
     * @param needExpress 是否需要发快递
     * @param waybill     运单号，需要发快递时填写
     * @return Result<?>
     * 2024/10/11 11:23:26
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> outRepairShip(long taskId, boolean needExpress, String waybill, String comment) {
        Task currentTask = getById(taskId);
        PhoneState currentState = currentTask.getAfterSale() ? PhoneState.WAIT_OUT_REPAIR_SHIP_IN_AS : PhoneState.WAIT_OUT_REPAIR_SHIP;
        if (needExpress) {
            // 需要发快递的情况
            if (StringUtils.isBlank(waybill)) {
                return Result.error("运单号不能为空");
            }
            // 添加发货记录
            packageMapper.insert(new Package(true, currentTask.getPhoneId(), basicService.getCurrentStaffId(), PackageType.REPAIR_SHIP, waybill, null, null));
        }

        // 分配给自己接收送修手机的任务
        assignTask(currentTask.getPhoneId(), basicService.getCurrentStaffId(), TaskType.OUTSOURCE_REPAIR_SIGN, currentTask);
        // 设置手机状态为送修待签收
        PhoneState state = currentTask.getAfterSale() ? PhoneState.WAIT_OUT_REPAIR_SIGN_IN_AS : PhoneState.WAIT_OUT_REPAIR_SIGN;
        finishTask(currentTask, state, comment, currentState);
        return Result.ok("操作成功");
    }

    /**
     * 库管员签收送修的手机
     *
     * @return Result<?>
     * 2024/10/11 11:31:32
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> outRepairSign(long taskId, String comment) {
        Task currentTask = getById(taskId);
        PhoneState currentState = currentTask.getAfterSale() ? PhoneState.WAIT_OUT_REPAIR_SIGN_IN_AS : PhoneState.WAIT_OUT_REPAIR_SIGN;
        // 1. 校验是否之前为快递送修
        int shipCount = packageMapper.getCountByPhoneIdAndType(currentTask.getPhoneId(), PackageType.REPAIR_SHIP);
        if (shipCount != 0) {
            // 若之前发过货
            int receiveCount = packageMapper.getCountByPhoneIdAndType(currentTask.getPhoneId(), PackageType.REPAIR_RECEIVE);
            // 且发货次数大于签收次数，说明之前为快递送修，应该添加签收记录
            if (shipCount > receiveCount) {
                packageMapper.insert(new Package(false, currentTask.getPhoneId(), basicService.getCurrentStaffId(), PackageType.REPAIR_RECEIVE, null, null, null));
            }
        }

        // 2. 将手机到货状态设置为送修到货
        phoneMapper.updatePhoneReceiveType(currentTask.getPhoneId(), PhoneReceiveType.OUT_REPAIR_STOCK);
        // 分配给维修组长送修完成填报任务
        assignTask(currentTask.getPhoneId(), basicService.getGlobalSetting().getRepairLeaderId(), TaskType.FILL_OUTSOURCE_REPAIR_RESULT, currentTask);

        // 设置手机状态为待填报送修结果
        PhoneState state = currentTask.getAfterSale() ? PhoneState.WAIT_FILL_OUT_REPAIR_RESULT_IN_AS : PhoneState.WAIT_FILL_OUT_REPAIR_RESULT;
        finishTask(currentTask, state, comment, currentState);
        return Result.ok("操作成功");
    }

    /**
     * 库管员接收维修完成的手机
     *
     * @param staffId 员工id：任务为售后单时，为库管员id（通常为自己），否则为采购id
     * @return Result<?>
     * 2024/10/11 16:31:31
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> receiveRepairedPhone(long taskId, long staffId, String comment) {
        Task currentTask = getById(taskId);
        Staff staff = staffMapper.selectById(staffId);
        PhoneState currentState = currentTask.getAfterSale() ? PhoneState.WAIT_RECEIVE_REPAIR_IN_AS : PhoneState.WAIT_RECEIVE_REPAIR;
        if (currentTask.getAfterSale()) {
            // 为售后单，分派售后维修发货任务
            if (!staff.getWarehouser()) {
                return Result.error("指定的员工不为库管员");
            }
            assignTask(currentTask.getPhoneId(), staffId, TaskType.SHIP_WHEN_AFTER_SALE_FINISHED, currentTask);
            // 手机状态设置为售后完成待发货
            phoneService.updatePhoneState(currentTask.getPhoneId(), PhoneState.WAIT_SHIP_IN_AS_FINISHED, currentState);
        } else {
            // 非售后单，分派采购员二次检查任务
            if (!staff.getPurchaser()) {
                return Result.error("指定的员工不为采购员");
            }
            assignTask(currentTask.getPhoneId(), staffId, TaskType.RECHECK, currentTask);
            // 手机状态设置为二次检查中
            phoneService.updatePhoneState(currentTask.getPhoneId(), PhoneState.RE_CHECKING, currentState);
        }
        finishTask(currentTask, comment);
        return Result.ok("操作成功");
    }


    /**
     * 转调任务
     *
     * @param taskId  待转调的任务id
     * @param staffId 转调的任务接收人id
     * @return Result<?>
     * 2024/10/12 11:09:20
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> reassignTask(long taskId, long staffId) {
        // 检查任务状态
        Task task = getById(taskId);
        if (task == null) {
            return Result.error("任务不存在");
        }
        if (task.getState() != TaskState.WAIT_RECEIVE) {
            return Result.error("待转调的任务状态异常，应为：待接收！");
        }
        if (task.getReceiverId() == staffId) {
            return Result.error("不能转调给同一个员工！");
        }

        Staff staff = staffMapper.selectById(staffId);
        PermissionCombine permissionCombine = PermissionCombine.of(PermissionTarget.TASK, PermissionType.REASSIGN);
        // 根据任务的类型，检查是否有权转调任务，并确定检查员工是否能接收任务
        switch (AssignablePositionType.of(task.getType())) {
            // 对于库管的任务
            case WAREHOUSER -> {
                permissionService.checkPermissionToDept(DepartmentType.WAREHOUSE, permissionCombine);
                if (!staff.getWarehouser()) {
                    return Result.error("接收转调任务的员工不为库管员");
                }
            }
            // 对于采购的任务
            case PURCHASER -> {
                permissionService.checkPermissionToDept(DepartmentType.PURCHASE, permissionCombine);
                if (!staff.getPurchaser()) {
                    return Result.error("接收转调任务的员工不为采购员");
                }
            }
            // 对于维修组长的任务
            case REPAIR_LEADER -> {
                permissionService.checkPermissionToDept(DepartmentType.REPAIR, permissionCombine);
                if (basicService.getGlobalSetting().getRepairLeaderId() != staffId) {
                    return Result.error("接收转调任务的员工不为维修组长");
                }
            }
            // 对于维修员的任务
            case REPAIR -> {
                permissionService.checkPermissionToDept(DepartmentType.REPAIR, permissionCombine);
                if (!staff.getMaintainer()) {
                    return Result.error("接收转调任务的员工不为维修员");
                }
            }
            // 对于渠道负责人的任务
            case TEAM_LEADER -> {
                permissionService.checkPermissionToTeam(task.getTeamId(), permissionCombine);
                if (!teamMapper.isTeamLeader(staffId, task.getTeamId())) {
                    return Result.error("接收转调任务的员工不为渠道负责人");
                }
            }
            // 对于渠道中普通员工的任务
            case TEAM_STAFF -> {
                permissionService.checkPermissionToTeam(task.getTeamId(), permissionCombine);
                TeamPositionType positionType = TeamPositionType.convert(task.getType());
                Result<?> result = positionService.checkPosition(staffId, task.getTeamId(), positionType);
                if (result.isError()) {
                    return result;
                }
            }
        }


        // 转调任务时，新分配的任务的上一个任务应该是转调任务的上一个任务
        assignTask(task.getPhoneId(), staffId, task.getType(), getById(task.getLastTaskId()));

        // 移除任务接收超时定时器
        ScheduledFuture<?> scheduledTask = scheduledTasks.remove(task.getId());
        if (scheduledTask != null) {
            scheduledTask.cancel(false);
        }

        // 将状态设置为已转调
        taskMapper.updateTaskState(taskId, TaskState.REASSIGNED.getValue());

        // 设置任务完成时间
        taskMapper.setTaskComplete(taskId);

        return Result.ok("操作成功");
    }

    /**
     * 销售发货
     *
     * @param waybill 发货运单号
     * @return Result<?>
     * 2024/10/13 14:02:35
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> saleShip(long taskId, @NotBlank String waybill, String comment) {
        Task currentTask = getById(taskId);
        Long phoneId = currentTask.getPhoneId();
        // 添加发货记录
        packageMapper.insert(new Package(true, phoneId, basicService.getCurrentStaffId(), PackageType.SALES_SHIP, waybill, null, null));
        // 添加收入结算记录，金额为上架时的金额
        miscService.addFinanceRecord(phoneId, phonePublishSoldMapper.getLastPublishPriceById(phoneId), FinanceSettlementType.SALES_INCOME, "销售收入");
        // 手机状态设置为销售收入待结算
        finishTask(currentTask, PhoneState.WAIT_SETTLE_WHEN_SALE, comment, PhoneState.OUTBOUND_ONLY);
        return Result.ok("操作成功");
    }

    /**
     * 接收售后
     * @return Result<?>
     * 2024/10/14 10:27:11
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> receiveAftersale(long taskId, String comment) {
        Task currentTask = getById(taskId);
        // 给维修组长分配售后维修评估任务
        assignTask(currentTask.getPhoneId(), basicService.getGlobalSetting().getRepairLeaderId(), TaskType.REPAIR_EVALUATE, currentTask);
        // 设置手机状态为售后维修评估中
        finishTask(currentTask, PhoneState.REPAIR_EVALUATING_IN_AS, comment, PhoneState.WAIT_RECEIVE_IN_AS);
        return Result.ok("操作成功");
    }

    /**
     * 售后结束发货
     *
     * @return Result<?>
     * 2024/10/14 10:38:42
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> shipAftersalePhone(long taskId, @NotBlank String waybill, String comment) {
        Task currentTask = getById(taskId);
        Long phoneId = currentTask.getPhoneId();
        // 添加发货记录
        packageMapper.insert(new Package(true, phoneId, basicService.getCurrentStaffId(), PackageType.AFTER_SALES_SHIP, waybill, null, null));
        // 手机状态设置为售后已发货
        finishTask(currentTask, PhoneState.SHIPPED_IN_AS, comment, PhoneState.WAIT_SHIP_IN_AS_FINISHED);
        return Result.ok("操作成功");
    }
}
